package com.example.system.service.contest.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.core.constants.Constants;
import com.example.common.core.domain.R;
import com.example.common.core.enums.ResultCode;
import com.example.common.security.exception.ServiceException;
import com.example.system.domain.contest.Contest;
import com.example.system.domain.contest.ContestToQuestion;
import com.example.system.domain.contest.DTO.ContestAddDTO;
import com.example.system.domain.contest.DTO.ContestEditDTO;
import com.example.system.domain.contest.DTO.ContestQueryDTO;
import com.example.system.domain.contest.DTO.ContestQuestionADDDTO;
import com.example.system.domain.contest.vo.ContestDetailVO;
import com.example.system.domain.contest.vo.ContestVO;
import com.example.system.domain.question.Question;
import com.example.system.domain.question.vo.QuestionVO;
import com.example.system.manager.ContestCacheManager;
import com.example.system.mapper.contest.ContestMapper;
import com.example.system.mapper.contest.ContestQuestionMapper;
import com.example.system.mapper.question.QuestionMapper;
import com.example.system.service.contest.ContestService;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

@Slf4j
@Service
public class ContestServiceImpl extends ServiceImpl<ContestQuestionMapper, ContestToQuestion> implements ContestService {
    @Autowired
    private ContestMapper contestMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ContestQuestionMapper contestQuestionMapper;

    @Autowired
    private ContestCacheManager cacheManager;

    @Override
    public List<ContestVO> list(ContestQueryDTO contestQueryDTO) {
        PageHelper.startPage(contestQueryDTO.getPageNum(), contestQueryDTO.getPageSize());
        return contestMapper.selectContestList(contestQueryDTO);
    }

    @Override
    public R<String> add(ContestAddDTO contestAddDTO) {
        checkContest(contestAddDTO,null);
        Contest contest = new Contest();
        BeanUtil.copyProperties(contestAddDTO, contest);
        if (contestMapper.insert(contest) == 0) {
            return R.fail(ResultCode.FAILED);
        }
        return R.ok(contest.getId().toString());
    }



    @Override
    public boolean addQuestion(ContestQuestionADDDTO contestQuestionADDDTO) {
        Contest contest = getContest(contestQuestionADDDTO.getContestId());
        LinkedHashSet<Long> questionIdSet = contestQuestionADDDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        int num = 1;
        List<ContestToQuestion> contestToQuestionList = new ArrayList<>();
        for (Long questionId : questionIdSet) {
            Question question = questionMapper.selectById(questionId);
            if (question == null) {
                throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
            }

            ContestToQuestion contestToQuestion = new ContestToQuestion();
            contestToQuestion.setContestId(contest.getId());
            contestToQuestion.setQuestionId(question.getId());
            contestToQuestion.setQuestionOrder(num++);
            contestToQuestionList.add(contestToQuestion);

        }
        return saveBatch(contestToQuestionList);


    }

    @Override
    public R<ContestDetailVO> detail(Long id) {
        Contest contest = getContest(id);
        ContestDetailVO contestDetailVO = new ContestDetailVO();
        BeanUtil.copyProperties(contest, contestDetailVO);
        List<QuestionVO> questionVOList = contestQuestionMapper.selecContestQuestionList(id);
        if (CollectionUtil.isEmpty(questionVOList)) {
            return R.ok(contestDetailVO);
        }
        contestDetailVO.setContestQuestionList(questionVOList);
        return R.ok(contestDetailVO);
//        List<Long> questionIdList = contestToQuestionList.stream().map(ContestToQuestion::getQuestionId).toList();
//        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
//                .select(Question::getId,Question::getTitle,Question::getDifficulty)
//                .in(Question::getId, questionIdList));
//        List<QuestionVO> questionVOList = questionList.stream().map(question -> {
//            QuestionVO questionVO = new QuestionVO();
//            BeanUtil.copyProperties(question, questionVO);
//            return questionVO;
//        }).toList();
//        ContestDetailVO contestDetailVO = new ContestDetailVO();
//        BeanUtil.copyProperties(contest, contestDetailVO);

    }

    @Override
    public int edit(ContestEditDTO contestEditDTO) {
        Contest contest = getContest(contestEditDTO.getId());
        if(Constants.TRUE.equals(contest.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkContest(contestEditDTO,contestEditDTO.getId());
        BeanUtil.copyProperties(contestEditDTO, contest);
        return contestMapper.updateById(contest);
    }

    @Override
    public int deleteQuestion(Long contestId, Long questionId) {
        Contest contest = getContest(contestId);
        checkContestTime(contest);
        return contestQuestionMapper.delete(new LambdaQueryWrapper<ContestToQuestion>()
                .eq(ContestToQuestion::getContestId, contestId)
                .eq(ContestToQuestion::getQuestionId, questionId));
    }

    @Override
    public int delete(Long id) {
        Contest contest = getContest(id);
        if(Constants.TRUE.equals(contest.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkContestTime(contest);
        contestQuestionMapper.delete(new LambdaQueryWrapper<ContestToQuestion>().eq(ContestToQuestion::getContestId,id));
        return contestMapper.deleteById(id);
    }

    @Override
    public int publish(Long id) {
        Contest contest = getContest(id);
        checkContestTime(contest);

        Long count = contestQuestionMapper.selectCount(new LambdaQueryWrapper<ContestToQuestion>().eq(ContestToQuestion::getContestId,id));
        if (count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        contest.setStatus(Constants.TRUE);

        cacheManager.addCache(contest);

        return contestMapper.updateById(contest);
    }

    @Override
    public int cancelPublish(Long id) {
        Contest contest = getContest(id);
        checkContestTime(contest);
        if (contest.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        contest.setStatus(Constants.FALSE);
        cacheManager.deleteCache(id);
        return contestMapper.updateById(contest);
    }

    private Contest getContest(Long id) {
        Contest contest = contestMapper.selectById(id);
        if (contest == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return contest;
    }
    private void checkContest(ContestAddDTO contestAddDTO, Long id) {
        List<Contest> contestList = contestMapper.selectList(new LambdaQueryWrapper<Contest>()
                .eq(Contest::getTitle, contestAddDTO.getTitle())
                .ne(id!= null, Contest::getId, id));
        if (CollectionUtil.isNotEmpty(contestList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (contestAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if (contestAddDTO.getEndTime().isBefore(contestAddDTO.getStartTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    private void checkContestTime(Contest contest) {
        if (contest.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

}
